package com.zyx.concurrentdemo.mytest.c04;

import com.zyx.concurrentdemo.c02basic.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

/**
 * @author Yaxi.Zhang
 * @since 2021/10/15 14:28
 * desc: 保护性暂停案例
 */
@Slf4j(topic = "c.GuardSusDemo")
public class GuardSusDemo {

    public static void main(String[] args) {
        // 居民等待收取消息
        for (int i = 0; i < 3; i++) {
            new Resident().start();
        }
        log.debug("准备发送信息...");
        Sleeper.sleep(6);
        for (Integer id : Mailboxes.getIds()) {
            new Postman(id, "msg =====>>>>> " + id).start();
        }
    }

}

@Slf4j(topic = "c.Resident")
class Resident extends Thread {
    @Override
    public void run() {
        GuardedObject go = Mailboxes.createGuardedObject();
        Object response = go.get(5000);
        log.debug("获取消息 =====>>>>> {}", response);
    }
}

@Slf4j(topic = "c.Postman")
class Postman extends Thread {

    private final int pid;
    private final String msg;

    public Postman(int pid, String msg) {
        this.pid = pid;
        this.msg = msg;
    }

    // 获取GuardedObject并发送信息
    @Override
    public void run() {
        log.debug("邮递员{}开始发送信息:{}", pid, msg);
        GuardedObject go = Mailboxes.getGuardedObject(pid);
        go.complete(msg);
    }
}


class Mailboxes {
    private final static Map<Integer, GuardedObject> BOXES = new Hashtable<>();

    private static volatile int mid;
    
    /**
     * 创建唯一的id
     */
    private static synchronized int generateId() {
        return mid++;
    }

    public static GuardedObject getGuardedObject(int id) {
        // 从map 中获取指定id对应的GuardedObject
        synchronized (BOXES) {
            return BOXES.remove(id);
        }
    }

    public static GuardedObject createGuardedObject() {
        GuardedObject go = new GuardedObject(generateId());
        synchronized (BOXES) {
            BOXES.put(go.getGid(), go);
        }
        return go;
    }

    public static Set<Integer> getIds() {
        synchronized (BOXES) {
            return BOXES.keySet();
        }
    }

}

class GuardedObject {

    private int gid;
    private Object response;

    public GuardedObject(int gid) {
        this.gid = gid;
    }

    public int getGid() {
        return gid;
    }

    public Object get(long timeout) {
        // 获取消息
        synchronized (this) {
            long begin = System.currentTimeMillis();
            long pastTime = 0;
            // 等待消息发送
            while (response == null) {
                if (timeout <= pastTime) {
                    break;
                }
                try {
                    // 防止虚假唤醒
                    this.wait(timeout - pastTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                pastTime = System.currentTimeMillis() - begin;
            }
        }
        return response;
    }

    public void complete(Object obj) {
        // 完成消息发送
        synchronized (this) {
            this.response = obj;
            this.notifyAll();
        }
    }
}